import UIKit
//闭包是自包含的函数代码块。Swift中的闭包与C和OC中的代码块以及其他语言的lambdas函数相识。
//闭包采取如下三种形式：
//1.全局函数一个有名字但不会捕获任何值的闭包
//2.嵌套函数是一个有名字可以捕获其封闭函数域内值的闭包
//3.闭包表达式是一个利用轻量级语法所写的可以捕获其上下文常量或变量的匿名闭包
//闭包表达式是一种利用简洁语法构建内联闭包的方式。

//sorted函数
let names = ["Chris", "Alex", "Eva", "Barry", "Daniella"]
func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}

let result = names.sorted()
print(result)

//闭包表达式语法
//{
//    (params) -> return Type in
//    statements
//}
//闭包表达式语法可以使用常量、变量和inout类型作为参数，不提供默认值。也可以在参数列表最后使用可变参数。
//revered = mySort(names, (s1: String, s2: String) -> Bool in
//return s1 > s2})

////根据上下文推断
//revered = mySort(names, {s1, s2} in  return s1 > s2)

//// 单表达式闭包隐式返回,可以隐藏return 关键字
//revered = mySort(names, {s1, s2} in s1 > s2)

////参数名称缩写 $0 和 $1 表示闭包中的第一个和第二个String类型的参数
//revered = mySort(name, { $0 > $1 })

//运算符函数. Swift中的String类型定义了关于大于号（>）的字符串实现。其作为一个函数接收两个String类型的参数并返回Bool类型的值。而这正好与sorted函数的第二个参数需要的函数类型相符合。因此可以简单用个>,Swift可以自动推断出您想使用大于号的字符串函数的实现。
//revered = mySort(name, >)

//尾随闭包 将一个很长的闭包表达式作为最后一个参数传递给函数，可以使用尾随闭包增加函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式，函数支持将其作为最后一个参数调用
func someFunctionThatTakeAClosure(closure: () -> ()) {
    //函数体部分
}

someFunctionThatTakeAClosure(closure: {
    
})

//只有一个闭包参数时可以这么写
someFunctionThatTakeAClosure {
    
}

let digitName = [
    0 : "zero", 1:"one", 2:"two", 3:"three", 4:"four",
    5 : "five", 6:"six", 7:"seven", 8:"eight", 9:"nine"
]

let numbers = [16, 58, 510]
let result1 = numbers.map { (num: Int) -> String in
   
       var number = num
       var output = ""
       while number > 0 {
           output = digitName[number % 10]! + output
           number = number / 10
       }
       return output
   
}

//闭包参数，返回值可以省略
let r1 = numbers.map { num in
    var number = num
    var output = ""
    while number > 0 {
        output = digitName[number % 10]! + output
        number = number / 10
    }
    return output
}
//捕获值 闭包可以在其定义的上下文中捕获常量或变量，
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount //函数体内能访问runningTotal 和 amount。是因为通过捕获在包含它的函数体（makeIncrementor）内已经存在的runningTotal 和 amount变量。
     
        return runningTotal
    }
    return incrementor
}

let incrementorByTen = makeIncrementor(forIncrement: 10)
incrementorByTen()
incrementorByTen()
incrementorByTen()

let incrementorBySeven = makeIncrementor(forIncrement: 7)
incrementorBySeven()
incrementorBySeven()
incrementorBySeven()
incrementorByTen()
//闭包是引用类型 上面incrementorByTen 和 incrementorBySeven都是常量，但是这些常量指向的闭包仍可以访问增加其捕获的变量值，这是因为函数或闭包是引用类型。无论你把函数或闭包赋值给一个常量或变量，实际上都是将常量或变量设置为函数或闭包的引用。这也意味着如果你将函数或闭包赋值给了两个不同的常量或变量，两个值都会指向同一个背包。
let alsoIncrementByTen = incrementorByTen
alsoIncrementByTen()

//额外
func sumadd(a:Int, b:Int, cb:(_ m:Int, _ n:Int) -> Int) -> Void {
    let sum = cb(a, b)
    print("sum is \(sum)")
}

sumadd(a: 1, b: 2) { (m, n) in
    return m + n
}


let arr = ["apple", "ac","bcd", "ggg","abcdae"]
let arr1 = arr.map { //添加导航栏右侧返回按钮
    str in
    return "orgin_" + str
}

let arr2 = arr.filter { //添加导航栏右侧返回按钮
    (str:String) -> Bool in
    str.hasPrefix("a")
}


let arr3 = arr.flatMap { $0 }

let arr4 = [1,2,3,4,5,6,7,8,9,10].reduce(into: 2) { partialResult, str in
    partialResult += Int(str)
}
print("arr4 is \(arr4)")
